package org.zoomdev.zoom.aop.factory.impl;

import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.Type;
import org.zoomdev.zoom.aop.Getter;
import org.zoomdev.zoom.aop.factory.GetterFactory;
import org.zoomdev.zoom.aop.utils.AsmUtils;
import org.zoomdev.zoom.common.caster.Caster;
import org.zoomdev.zoom.common.exceptions.ZoomException;
import org.zoomdev.zoom.common.utils.Classes;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;

public final class GetterFactoryImpl extends AsmSingleMethodFactory<Getter, Field> implements GetterFactory {


    public GetterFactoryImpl(ClassLoader classLoader) {
        super(classLoader);
    }

    @Override
    protected String getClassName(Field param) {
        return param.getDeclaringClass().getName() + "." + param.getName() + "$Getter";
    }

    @Override
    protected Getter createImpl(Field field) {
        if (!Modifier.isPublic(field.getModifiers())) {
            Method getter = Classes.getGetter(field.getDeclaringClass(), field.getName());
            if (getter == null) {
                return new FieldGetter(field);
            }
        }
        return super.createImpl(field);
    }

    @Override
    protected byte[] createBytes(String className, Field field) {
        String declaringClassInternalName = Type.getInternalName(field.getDeclaringClass());
        String fieldTypeDescriptor = Type.getDescriptor(field.getType());
        boolean isStatic = Modifier.isStatic(field.getModifiers());
        boolean isPublic = Modifier.isPublic(field.getModifiers());

        ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES | ClassWriter.COMPUTE_MAXS);

        // 类定义
        cw.visit(AsmUtils.VERSION, Opcodes.ACC_PUBLIC, className, null,
                "java/lang/Object", new String[]{Type.getInternalName(Getter.class)});

        // 构造方法
        MethodVisitor mv = cw.visitMethod(Opcodes.ACC_PUBLIC, "<init>", "()V", null, null);
        mv.visitCode();
        mv.visitVarInsn(Opcodes.ALOAD, 0);
        mv.visitMethodInsn(Opcodes.INVOKESPECIAL, "java/lang/Object", "<init>", "()V", false);
        mv.visitInsn(Opcodes.RETURN);
        mv.visitMaxs(1, 1);
        mv.visitEnd();

        // get 方法
        mv = cw.visitMethod(Opcodes.ACC_PUBLIC, "get", "(Ljava/lang/Object;)Ljava/lang/Object;", null, null);
        mv.visitCode();

        try {
            if (isPublic) {
                // 处理public字段（静态/实例）
                if (isStatic) {
                    mv.visitFieldInsn(Opcodes.GETSTATIC, declaringClassInternalName,
                            field.getName(), fieldTypeDescriptor);
                } else {
                    mv.visitVarInsn(Opcodes.ALOAD, 1);
                    mv.visitTypeInsn(Opcodes.CHECKCAST, declaringClassInternalName);
                    mv.visitFieldInsn(Opcodes.GETFIELD, declaringClassInternalName,
                            field.getName(), fieldTypeDescriptor);
                }
            } else {
                // 处理非public字段（通过getter方法）
                Method getter = Classes.getGetter(field.getDeclaringClass(), field.getName());
                if (getter == null) {
                    throw new ZoomException("无法为[" + field.getDeclaringClass().getName() + "." + field.getName() + "]创建getter,无对应public字段或者方法");
                }

                boolean isGetterStatic = Modifier.isStatic(getter.getModifiers());
                if (isGetterStatic) {
                    mv.visitMethodInsn(Opcodes.INVOKESTATIC, declaringClassInternalName,
                            getter.getName(), Type.getMethodDescriptor(getter), false);
                } else {
                    mv.visitVarInsn(Opcodes.ALOAD, 1);
                    mv.visitTypeInsn(Opcodes.CHECKCAST, declaringClassInternalName);
                    mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, declaringClassInternalName,
                            getter.getName(), Type.getMethodDescriptor(getter), false);
                }
            }

            // 基本类型装箱处理
            if (field.getType().isPrimitive()) {
                Type boxedType = Type.getType(Caster.getWrapClass(field.getType()));
                String valueOfDesc = String.format("(%s)%s",
                        fieldTypeDescriptor,
                        boxedType.getDescriptor());

                mv.visitMethodInsn(Opcodes.INVOKESTATIC,
                        boxedType.getInternalName(),
                        "valueOf",
                        valueOfDesc,
                        false);
            }

            mv.visitInsn(Opcodes.ARETURN);
            mv.visitMaxs(2, 2);
        } catch (Exception e) {
            throw new ZoomException("Bytecode generation failed", e);
        } finally {
            mv.visitEnd();
        }

        cw.visitEnd();
        return cw.toByteArray();
    }

    static class FieldGetter implements Getter {

        private final Field field;

        FieldGetter(Field field) {
            this.field = field;
            field.setAccessible(true);
        }

        @Override
        public Object get(Object target) {
            try {
                return field.get(target);
            } catch (IllegalAccessException e) {
                throw new ZoomException("无法获取值" + field, e);
            }
        }
    }
}
